package com.lwj.interceptor;

import com.lwj.annotation.SensitiveEntity;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;

/**
 * 加密拦截器
 * 注意@Component注解一定要加上
 *
 * 在mybatis中可被拦截的类型有四种(按照拦截顺序)：
 * 1: Executor：拦截执行器的方法。
 *    可拦截方法：update, query, flushStatements, commit, rollback,getTransaction, close, isClosed
 * 2: ParameterHandler：拦截参数的处理。
 *    可拦截方法： getParameterObject, setParameters
 * 3: StatementHandler：拦截Sql语法构建的处理。
 *    可拦截方法：prepare, parameterize, batch, update, query
 * 4: ResultHandler：拦截结果集的处理。
 *    可拦截方法：handleResultSets, handleOutputParameters
 *
 * @Intercepts：标识该类是一个拦截器；
 * @Signature：指明自定义拦截器需要拦截哪一个类型，哪一个方法(type:类  method+args：方法签名)；
 *
 *
 * @date : 2020/1/19.
 */
@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
public class EncryptInterceptor implements Interceptor {
    protected static final Logger logger = LoggerFactory.getLogger(EncryptInterceptor.class);
    private final EncryptUtil encryptUtil;

    @Autowired
    public EncryptInterceptor(EncryptUtil encryptUtil) {
        this.encryptUtil = encryptUtil;
    }


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //@Signature 指定了 type= parameterHandler 后，这里的 invocation.getTarget() 便是parameterHandler
        //若指定ResultSetHandler ，这里则能强转为ResultSetHandler
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        // 获取参数对像，即 mapper 中 paramsType 的实例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        //取出实例
        Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass = parameterObject.getClass();
            //校验该实例的类是否被@SensitiveData所注解
            SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveEntity.class);
            if (Objects.nonNull(sensitiveEntity)) {
                //取出当前当前类所有字段，传入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                encryptUtil.encrypt(declaredFields, parameterObject);
            }
        }
        return invocation.proceed();
    }

    /**
     * 切记配置，否则当前拦截器不会加入拦截器链
     */
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    //自定义配置写入，没有自定义配置的可以直接置空此方法
    @Override
    public void setProperties(Properties properties) {
    }
}
